{
module BJKLexer  
open System
open System.Collections.Generic
open Microsoft.FSharp.Text.Lexing
open BJKParser

let keywords =   
    [   
        "loop", LOOP;   
        "Loop", LOOP;   
        "if", IF;   
        "If", IF;   
        "choose", CHOOSE;   
        "Choose", CHOOSE;   
        "else", ELSE;   
        "else", ELSE;   
        "or", OR;   
        "Or", OR;   
        "skip", SKIP;  
        "Skip", SKIP;  
    ] |> Map.ofList   
 
let ops =   
    [   
        ":=",  ASSIGN;   
        "+",   PLUS;   
        "*",   TIMES;   
    ] |> Map.ofList 

(* internal lexer definitions *)
let pos = ref 0
let varIndex = ref 0
let varString = ref ""
let error = fun x -> printfn("%A\n") x 

(* hash table from string to int - to store the variables found during parse *)
(* the hash table indetifier *)  
let varTable  = new Dictionary<string, int>()

(* if variable was already encountered - returns its integer index, 
   otherwise assigns variable with a fresh index and returns this index *)
let rec getVarIndex (varId : string, varTable : Dictionary<string, int>) = 
      let newVarIndex(varId : string) =
           let _ = varIndex := !(varIndex : int ref) + 1
           let _ = (varTable.Add(varId, !varIndex))
           let _ = (varString := (!(varString : string ref) + varId + " = " + (!varIndex).ToString() + "\n"))
           getVarIndex(varId, varTable)
      if (varTable.ContainsKey(varId)) then varTable.[varId]
      else newVarIndex(varId) 
}   

let char        = ['a'-'z' 'A'-'Z']
let digit       = ['0'-'9']   
let identifier  = char(char|digit|['-' '_'])*   
let whitespace  = [' ' '\t']   
let newline     = "\n\r" | '\n' | '\r'  
let operator    = ":=" | "+" | "*"
let comment		= '%'[^'\n']*'\n'

rule tokenize = parse   
| comment	    { lexbuf.EndPos <- lexbuf.EndPos.NextLine; tokenize lexbuf; }
| whitespace    { tokenize lexbuf }   
| newline	    { lexbuf.EndPos <- lexbuf.EndPos.NextLine; tokenize lexbuf; } 
| operator      { ops.[LexBuffer<_>.LexemeString lexbuf] }   			
| identifier    { match keywords.TryFind(LexBuffer<_>.LexemeString lexbuf) with   
                        | Some(token) -> token   
                        | None -> VAR(getVarIndex((LexBuffer<_>.LexemeString lexbuf), varTable)) }   
| "0"			{ ZERO }
| ';'           { SEMI }  
| '('           { LPAREN }
| ')'           { RPAREN }
| '{'           { LBRACE }
| '}'           { RBRACE } 
| eof           { EOF }